package org.zhaolei.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.zhaolei.config.RabbitMQConfig;
import org.zhaolei.enums.*;
import org.zhaolei.exception.BizException;
import org.zhaolei.feign.CouponFeignService;
import org.zhaolei.feign.ProductFeignService;
import org.zhaolei.feign.UserFeignService;
import org.zhaolei.interceptor.LoginInterceptor;
import org.zhaolei.mapper.ProductOrderItemMapper;
import org.zhaolei.mapper.ProductOrderMapper;
import org.zhaolei.model.LoginUser;
import org.zhaolei.model.OrderMessage;
import org.zhaolei.model.ProductOrderDO;
import org.zhaolei.model.ProductOrderItemDO;
import org.zhaolei.request.ConfirmOrderRequest;
import org.zhaolei.request.LockCouponRecordRequest;
import org.zhaolei.request.LockProductRequest;
import org.zhaolei.request.OrderItemRequest;
import org.zhaolei.service.ProductOrderService;
import org.zhaolei.util.CommonUtil;
import org.zhaolei.util.JsonData;
import org.zhaolei.vo.CouponRecordVO;
import org.zhaolei.vo.OrderItemVO;
import org.zhaolei.vo.ProductOrderAddressVO;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

    @Autowired
    private ProductOrderMapper productOrderMapper;

    @Autowired
    private ProductOrderItemMapper productOrderItemMapper;

    @Autowired
    private UserFeignService userFeignService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    /**
     * * 防重提交
     * * 用户微服务-确认收货地址
     * * 商品微服务-获取最新购物项和价格
     * * 订单验价
     *   * 优惠券微服务-获取优惠券
     *   * 验证价格
     * * 锁定优惠券
     * * 锁定商品库存
     * * 创建订单对象
     * * 创建子订单对象
     * * 发送延迟消息-用于自动关单
     * * 创建支付信息-对接三方支付
     * @param confirmOrderRequest
     * @return
     */
    @Override
    public JsonData confirmOrder(ConfirmOrderRequest confirmOrderRequest) {
        //获取当前用户信息和创建订单号
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);

        //根据地址id获取地址信息
        //由订单微服务调用用户微服务时会发生token传递失败的情况，即调用用户服务的接口时状态是未登录
        ProductOrderAddressVO addressVO = this. getUserAddress(confirmOrderRequest.getAddressId());

        log.info("收货地址信息：{}", addressVO);

        //获取用户加入购物车的商品，从请求参数里获取
        List<Long> productIdList = confirmOrderRequest.getProductIdList();
        //获取购物车商品后，清空了购物车，如果后面下单失败了，会导致用户购物车为空，如何避免这种情况呢？？？
        //利用延迟消息队列，商品服务获取订单号后，先发送一个消息到队列中，等到30秒或1分钟后，查询订单支付失败，则恢复购物车的商品，支付成功就不用恢复
        JsonData cartItemData = productFeignService.confirmOrderCartItem(productIdList);
        //通过调用商品服务，从购物车那里获取商品信息，并转换成下单的商品信息
        List<OrderItemVO> orderItemVOList = cartItemData.getData(new TypeReference<>() {});
        log.info("获取的商品:{}",orderItemVOList);
        if(orderItemVOList == null){
            //购物车商品不存在
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_CART_ITEM_NOT_EXIST);
        }
        //订单验价功能实现，减去优惠券的优惠价，每个订单只对应一个优惠券
        this.checkPrice(orderItemVOList, confirmOrderRequest);
        //锁定优惠券
        this.lockCouponRecords(confirmOrderRequest, orderOutTradeNo);
        //锁定库存
        this.lockProductsStocks(orderItemVOList, orderOutTradeNo);

        //创建订单（本质就是往对应的数据表里插入信息，结合上面的信息）
        ProductOrderDO productOrderDO = this.saveProductOrder(confirmOrderRequest, loginUser, orderOutTradeNo, addressVO);
        //创建订单中的商品项
        this.saveProductOrderItems(orderOutTradeNo, productOrderDO.getId(), orderItemVOList);
        //发送延迟消息，用于自动关单 TODO
        OrderMessage orderMessage = new OrderMessage();
        orderMessage.setOutTradeNo(orderOutTradeNo);
        rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(), orderMessage);
        log.info("发送延迟消息成功，消息体={}", orderMessage.toString());
        //创建支付  TODO
        return null;
    }

    private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemVOList) {
        List<ProductOrderItemDO> list = orderItemVOList.stream().map(
                obj->{
                    ProductOrderItemDO itemDO = new ProductOrderItemDO();
                    itemDO.setBuyNum(obj.getBuyNum());
                    itemDO.setProductId(obj.getProductId());
                    itemDO.setProductImg(obj.getProductImg());
                    itemDO.setProductName(obj.getProductTitle());

                    itemDO.setOutTradeNo(orderOutTradeNo);
                    itemDO.setCreateTime(new Date());

                    //单价
                    itemDO.setAmount(obj.getAmount());
                    //总价
                    itemDO.setTotalAmount(obj.getTotalAmount());
                    itemDO.setProductOrderId(orderId);
                    return itemDO;
                }
        ).collect(Collectors.toList());


        productOrderItemMapper.insertBatch(list);
    }

    private ProductOrderDO saveProductOrder(ConfirmOrderRequest confirmOrderRequest, LoginUser loginUser, String orderOutTradeNo, ProductOrderAddressVO addressVO) {
        ProductOrderDO productOrderDO = new ProductOrderDO();
        productOrderDO.setUserId(loginUser.getId());
        productOrderDO.setHeadImg(loginUser.getHeadImg());
        productOrderDO.setNickname(loginUser.getName());

        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);
        productOrderDO.setOrderType(ProductOrderTypeEnum.DAILY.name());
        //实际支付的总价
        productOrderDO.setPayAmount(confirmOrderRequest.getRealPayAmount());
        //优惠前总价
        productOrderDO.setTotalAmount(confirmOrderRequest.getTotalAmount());
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
//        ProductOrderTypeEnum.valueOf(confirmOrderRequest.getPayType()).name();
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(confirmOrderRequest.getPayType()).name());

        productOrderDO.setReceiverAddress(JSON.toJSONString(addressVO));

        productOrderMapper.insert(productOrderDO);

        return productOrderDO;
    }

    /**
     * 锁定商品库存
     * @param orderItemVOList
     * @param orderOutTradeNo
     */
    private void lockProductsStocks(List<OrderItemVO> orderItemVOList, String orderOutTradeNo) {
        //构造商品微服务入参然后调用其对应接口
        List<OrderItemRequest> itemRequestList = orderItemVOList.stream().map(obj->{
            OrderItemRequest itemRequest = new OrderItemRequest();
            itemRequest.setProductId(obj.getProductId());
            itemRequest.setBuyNum(obj.getBuyNum());
            return itemRequest;
        }).collect(Collectors.toList());

        LockProductRequest lockProductRequest = new LockProductRequest();
        lockProductRequest.setOrderOutTradeNo(orderOutTradeNo);
        lockProductRequest.setOrderItemList(itemRequestList);

        //调用服务
        JsonData jsonData = productFeignService.lockProductStock(lockProductRequest);
        if (jsonData.getCode() != 0) {
            log.error("锁定商品库存失败：{}",lockProductRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_LOCK_PRODUCT_FAIL);
        }
    }

    /**
     * 锁定优惠券
     * @param confirmOrderRequest
     * @param orderOutTradeNo
     */
    private void lockCouponRecords(ConfirmOrderRequest confirmOrderRequest, String orderOutTradeNo) {
        //构造优惠券微服务的入参然后通过openFeign调用优惠券微服务接口
        List<Long> lockCouponRecordIds = new ArrayList<>();
        if(confirmOrderRequest.getCouponRecordId() > 0) {
            lockCouponRecordIds.add(confirmOrderRequest.getCouponRecordId());

            LockCouponRecordRequest lockCouponRecordRequest = new LockCouponRecordRequest();
            lockCouponRecordRequest.setOrderOutTradeNo(orderOutTradeNo);
            lockCouponRecordRequest.setLockCouponRecordIds(lockCouponRecordIds);
            //发起请求
            JsonData jsonData = couponFeignService.lockCouponRecords(lockCouponRecordRequest);
            if(jsonData.getCode() != 0) {
                throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
            }
        }
    }

    /**
     * 验证商品价格
     * @param orderItemVOList
     * @param confirmOrderRequest
     */
    private void checkPrice(List<OrderItemVO> orderItemVOList, ConfirmOrderRequest confirmOrderRequest) {
        //根据orderItemVOList获取商品总价格
        BigDecimal realPayAmount = new BigDecimal("0");
        if (orderItemVOList != null) {
            for (OrderItemVO orderItemVO : orderItemVOList) {
                BigDecimal amount = orderItemVO.getTotalAmount();
                realPayAmount = realPayAmount.add(amount);
            }
        }

        //获取优惠券，并判断是否可以用，需要抽取出另一个函数
        CouponRecordVO couponRecordVO = getCartCouponRecord(confirmOrderRequest.getCouponRecordId());
        if (couponRecordVO != null) {
            //支付的金额不满足满减的要求，则不能使用优惠券
            if (realPayAmount.compareTo(couponRecordVO.getConditionPrice()) < 0) {
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
            }
            //优惠券金额大于支付金额，则支付金额为0
            if (couponRecordVO.getPrice().compareTo(realPayAmount) > 0) {
                realPayAmount = BigDecimal.ZERO;
            } else {
                realPayAmount = realPayAmount.subtract(couponRecordVO.getPrice());
            }
        }

        if(realPayAmount.compareTo(confirmOrderRequest.getRealPayAmount()) !=0 ){
            log.error("订单验价失败：{}",confirmOrderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }

    private CouponRecordVO getCartCouponRecord(Long couponRecordId) {
        if (couponRecordId == null || couponRecordId < 0) {
            return null;
        }

        JsonData couponRecordData = couponFeignService.findUserCouponRecordById(couponRecordId);
        if (couponRecordData.getCode() != 0) {
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
        }
        if (couponRecordData.getCode() == 0) {
            //判断优惠券的有效期
            //把数据库里存储的Coupon对象转换成CouponRecordVO对象
            CouponRecordVO couponRecordVO = couponRecordData.getData(new TypeReference<>() {});
            if (!couponAvailable(couponRecordVO)) {
                log.error("优惠券已过期");
                throw new BizException(BizCodeEnum.COUPON_UNAVAILABLE);
            }
            return couponRecordVO;
        }

        return null;
    }

    private boolean couponAvailable(CouponRecordVO couponRecordVO) {
        if (couponRecordVO.getUseState().equalsIgnoreCase(CouponStateEnum.NEW.name())) {
            long currentTimeStamp = CommonUtil.getCurrentTimestamp();
            long start = couponRecordVO.getStartTime().getTime();
            long end = couponRecordVO.getEndTime().getTime();
            if (currentTimeStamp >= start && currentTimeStamp <= end) {
                return true;
            }
        }
        return false;
    }

    private ProductOrderAddressVO getUserAddress(long addressId) {

        JsonData addressData = userFeignService.detail(addressId);
        if(addressData.getCode() !=0){
            log.error("获取收获地址失败,msg:{}",addressData);
            throw new BizException(BizCodeEnum.ADDRESS_NO_EXITS);
        }
//        ProductOrderAddressVO addressVO = (ProductOrderAddressVO) addressData.getData();
        ProductOrderAddressVO addressVO = addressData.getData(new TypeReference<>(){});
        //等效代码
//        String json = JSON.toJSONString(addressData.getData());
//        ProductOrderAddressVO addressVO = JSON.parseObject(json, ProductOrderAddressVO.class);
        return addressVO;
    }

    @Override
    public String queryProductOrderState(String outTradeNo) {
        //根据订单id获取订单对象
        //这里调用mybatisplus的内置函数，不用再在mapper里增加接口了
        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no",outTradeNo));

        if(productOrderDO == null){
            //订单不存在，返回空字符串
            return "";
        }else {
            return productOrderDO.getState();
        }
    }

    @Override
    public boolean closeProductOrder(OrderMessage orderMessage) {
        //根据订单id查到ProductOrderDO
        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no",orderMessage.getOutTradeNo()));
        //订单不存在或者已经支付，则直接确认消息
        if(productOrderDO == null){
            //订单不存在
            log.warn("直接确认消息，订单不存在:{}",orderMessage);
            return true;
        }
        if(productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())){
            //已经支付
            log.info("直接确认消息,订单已经支付:{}",orderMessage);
            return true;
        }

        //向第三方支付查询订单是否真的未支付  TODO

        String payResult = "";

        //从第三方支付处查到结果为空，则未支付成功，本地取消订单，订单状态改为CANCEL
        if(StringUtils.isBlank(payResult)){
            productOrderMapper.updateOrderPayState(productOrderDO.getOutTradeNo(),ProductOrderStateEnum.CANCEL.name(),ProductOrderStateEnum.NEW.name());
            log.info("结果为空，则未支付成功，本地取消订单:{}",orderMessage);
            return true;
        }else {
            //支付成功，主动的把订单状态改成UI已支付，造成该原因的情况可能是支付通道回调有问题
            log.warn("支付成功，主动的把订单状态改成UI就支付，造成该原因的情况可能是支付通道回调有问题:{}",orderMessage);
            productOrderMapper.updateOrderPayState(productOrderDO.getOutTradeNo(),ProductOrderStateEnum.PAY.name(),ProductOrderStateEnum.NEW.name());
            return true;
        }

    }
}
